namespace ETestIT.Core.Storage
{
    using System.Collections;
    using System.Web;

    public abstract class Store : IStorage
    {
        protected static readonly object AppStorageLock = new object();
        protected static readonly object LocalStorageLock = new object();
        protected static readonly object SessionStorageLock = new object();
        private static AppStorage _appStorage;
        private static LocalStorage _localStorage;
        private static SessionStorage _sessionStorage;

        protected Store()
        {
        }

        public static Store Application
        {
            get
            {
                if (_appStorage == null)
                {
                    lock (AppStorageLock)
                    {
                        if (_appStorage == null)
                        {
                            _appStorage = new AppStorage();
                        }
                    }
                }
                return _appStorage;
            }
        }

        public static bool IsWebApplication
        {
            get { return (HttpContext.Current != null); }
        }

        public static Store Local
        {
            get
            {
                if (_localStorage == null)
                {
                    lock (LocalStorageLock)
                    {
                        if (_localStorage == null)
                        {
                            _localStorage = new LocalStorage();
                        }
                    }
                }
                return _localStorage;
            }
        }

        protected abstract object LockInstance { get; }

        public static Store Session
        {
            get
            {
                if (_sessionStorage == null)
                {
                    lock (SessionStorageLock)
                    {
                        if (_sessionStorage == null)
                        {
                            _sessionStorage = new SessionStorage();
                        }
                    }
                }
                return _sessionStorage;
            }
        }

        protected abstract bool UseLocking { get; }
        protected abstract Hashtable GetInternalHashtable();

        public void Clear()
        {
            if (UseLocking)
            {
                lock (LockInstance)
                {
                    GetInternalHashtable().Clear();
                }
            }
            else
            {
                GetInternalHashtable().Clear();
            }
        }
        public void Remove(object key)
        {
            if (UseLocking)
            {
                lock (LockInstance)
                {
                    GetInternalHashtable().Remove(key);
                }
            }
            else
            {
                GetInternalHashtable().Remove(key);
            }
        }
        public void Set<T>(object key, T value)
        {
            if (UseLocking)
            {
                lock (LockInstance)
                {
                    GetInternalHashtable()[key] = value;
                }
            }
            else
            {
                GetInternalHashtable()[key] = value;
            }
        }
        public bool Contains(object key)
        {
            if (UseLocking)
            {
                lock (LockInstance)
                {
                    return GetInternalHashtable().ContainsKey(key);
                }
            }
            return GetInternalHashtable().ContainsKey(key);
        }
        public T Get<T>(object key)
        {
            if (UseLocking)
            {
                lock (LockInstance)
                {
                    return (T)GetInternalHashtable()[key];
                }
            }
            return (T)GetInternalHashtable()[key];
        }

        #region IStorage Members

        public T Get<T>(string key)
        {
            return Get<T>((object)key);
        }

        public void Set<T>(string key, T value)
        {
            Set((object)key, value);
        }

        public string GetString(string key)
        {
            return Get<string>(key);
        }

        public void SetString(string key, string value)
        {
            Set(key, value);
        }

        #endregion
    }
}